#include <Ape/Exception.h>
#include <Ape/String.h>
#include <Ape/Collections/LinkedList.h>
#include <Ape/Collections/StringList.h>
#include <Ape/IO/Console.h>

#include <Ape/MT/Thread.h>

#ifdef WITH_EXCEPTIONS

#if defined(LINUX_BACKTRACE)
#	include <execinfo.h>
#	include <cxxabi.h>
#	include <stdlib.h>
#	include <signal.h>
#endif

namespace Ape {
	Exception::Exception() :
			m_Msg (new String (L"") ),
			m_StackTrace (new Collections::StringList)
	{
		GetCurrentStackTrace (m_StackTrace);
	}
	Exception::Exception (const wchar_t* msg) :
			m_Msg (new String (msg) ),
			m_StackTrace (new Collections::StringList)
	{
		GetCurrentStackTrace (m_StackTrace);
	}
	Exception::Exception (const Exception& other)
			: Object (other),
			m_Msg (new String (*other.m_Msg) ),
			m_StackTrace (new Collections::StringList)
	{
		foreach (s, Collections::LinkedList<String>, * (other.m_StackTrace) ) {
			m_StackTrace->Append (s);
		}
	}
	Exception::~Exception() {
		del (m_Msg);
		del (m_StackTrace);
	}
	
	const String Exception::get_Message() const {
		return *m_Msg;
	}
	
	void Exception::_Dump (bool verbose) const {
		Object::_Dump (verbose);
		DUMP_NON_V_TYPE;
		
		char* pBuff = NULL;
		m_Msg->ToLatin1 (&pBuff);
		printf ("\t\tMessage:\t%s\n", pBuff);
		del_a (pBuff);
		
		if (verbose) {
			Ape::IO::StdOut << L"\t\tStack trace:" << Ape::IO::NewLine;
			foreach (stackLine, Collections::LinkedList<String>, *m_StackTrace) {
				Ape::IO::StdOut << L"\t\t\t" << (String) stackLine << Ape::IO::NewLine;
			}
		}
		
	}
	
	const String Exception::ToString() const {
		return get_Message();
	}
	
	void Exception::GetCurrentStackTrace (Collections::LinkedList<String>* list) {
#if defined(LINUX_BACKTRACE)
		void* aRoutineAddrs [LINUX_BACKTRACE_MAXSIZE];
		int iSize = backtrace (aRoutineAddrs, LINUX_BACKTRACE_MAXSIZE);
		char** aSymbols = backtrace_symbols (aRoutineAddrs, LINUX_BACKTRACE_MAXSIZE);
		for (int i = 0; i < iSize; i++) {
			//aSymbols[i]
			const char* csOriginal = aSymbols[i];
			//printf("original: %s\n", csOriginal);
			char csMangled[LINUX_BACKTRACE_MAXLINESIZE];
			char csDemangled[LINUX_BACKTRACE_MAXLINESIZE];
			size_t iLength = LINUX_BACKTRACE_MAXLINESIZE;
			int iStatus = 0;
			bool doRemember = false;
			int iMangledIndex = 0;
			
			for (int i = 0; i < 2048; i++) {
				if (csOriginal[i] == '(') {
					doRemember = true;
					continue;
				}
				if (csOriginal[i] == '+') {
					doRemember = false;
					continue;
				}
				if (csOriginal[i] == '\0')
					break;
					
				if (doRemember) {
					csMangled[iMangledIndex++] = csOriginal[i];
				}
			}
			csMangled[iMangledIndex] = '\0';
			//printf("Mangled: %s\n", csMangled);
			
			abi::__cxa_demangle (csMangled, csDemangled, &iLength, &iStatus);
			
			if (!iStatus)
				list->Append (csDemangled);
			else
				list->Append (csMangled);
		}
		free (aSymbols); // this piece of memory has been allocated by malloc
#else
#warning No backtrace implementation selected
#endif
	}
	
	const /*Collections::LinkedList<String>*/Collections::StringList& Exception::get_StackTrace() const {
		return *m_StackTrace;
	}
}

#endif // WITH_EXCEPTIONS
